package Hunters;

import AGENTS.AGENTS_EMFALCON;
import Bots.Bots;
import cz.cuni.amis.pogamut.base.communication.worldview.listener.annotation.ObjectClassEventListener;
import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
import cz.cuni.amis.pogamut.base3d.worldview.object.event.WorldObjectAppearedEvent;
import cz.cuni.amis.pogamut.ut2004.agent.module.utils.TabooSet;
import cz.cuni.amis.pogamut.ut2004.agent.navigation.NavigationState;
import cz.cuni.amis.pogamut.ut2004.agent.navigation.UT2004PathAutoFixer;
import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004Bot;
import cz.cuni.amis.pogamut.ut2004.bot.impl.UT2004BotModuleController;
import cz.cuni.amis.pogamut.ut2004.communication.messages.ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.UT2004ItemType;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Initialize;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Rotate;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.StopShooting;
import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.*;
import cz.cuni.amis.pogamut.ut2004.utils.UT2004BotRunner;
import cz.cuni.amis.utils.collections.MyCollections;
import cz.cuni.amis.utils.exception.PogamutException;
import cz.cuni.amis.utils.flag.FlagListener;

import java.util.ArrayList;
import java.util.logging.Level;

@AgentScoped
public class HunterBot_EMFALCON extends UT2004BotModuleController<UT2004Bot> {

    public int agt;

    int numSpace = 3;
    int CURSTATE = 0;
    int ACTION = 1;
    int REWARD = 2;


    static int numState = 28;
    static int numAction = 5;
    static int numReward = 2;

    static boolean immediateReward = false;

    public static Bots[] bots;
    public static AGENTS_EMFALCON[] agent;


    private final static int RFALCON = 0;
    private final static int TDFALCON = 1;
    private final static int BPN = 2;

    private final static int QLEARNING = 0;
    private final static int SARSA = 1;


    private static int TDMethod = QLEARNING;
    private static int AVTYPE = TDFALCON;


    private UT2004PathAutoFixer autoFixer;

    private static int instanceCount = 0;

    double lastheal;
    double lasthitdamage;

    //状态采样
    public void observe(Bots bots) {

        if (((double) info.getHealth() / (double) game.getMaxHealth()) > lastheal && !bots.isdead && bots.r < 0.25) {
            bots.r = 0.25;
        }
        bots.isdead = false;
    	/*
		if((double)bots.hitdamage>lasthitdamage && bots.r<0.8) {
			bots.r=0.8;

		}
    	*/

        bots.observe();

        //血量状态


        bots.stat[0] = (double) info.getHealth() / (double) game.getMaxHealth();
        if (bots.stat[0] > 1) bots.stat[0] = 1.0;
        bots.stat[1] = 1 - bots.stat[0];
        lastheal = bots.stat[0];
    	/*
    	
    	//子弹数量状态
    	bots.stat[2]=(double)info.getCurrentAmmo()/game.getMaxHighArmor();
    	if(bots.stat[2]>1)bots.stat[2]=1.0;
    	bots.stat[3]=1-bots.stat[2];
    	
    	//敌人距离状态
    	if(bots.enemy==null) {
    		return;
    	}else {
       	 	bots.distance = info.getLocation().getDistance(bots.enemy.getLocation());
       	 	bots.decentDistance= Math.round(random.nextFloat() * 800) + 200;
       	 	if(bots.decentDistance < bots.distance) {
           	 	bots.stat[4]=(double)bots.decentDistance/bots.distance;
            	if(bots.stat[4]>1)bots.stat[4]=1.0;
           	 	bots.stat[5]=1-bots.stat[4];
       	 	}
    	}
    	
    	*/
        if (bots.enemy != null) {
            bots.stat[2] = 0;
            bots.stat[3] = 1;
        }

        //看到敌人状态
        if (players.canSeeEnemies()) {
            bots.stat[6] = 0;
            bots.stat[7] = 1;
        }


        if (bots.shooting) {
            bots.stat[8] = 0;
            bots.stat[9] = 1;
        }

        if (!players.canSeeEnemies() && (info.isShooting() || info.isSecondaryShooting())) {
            getAct().act(new StopShooting());
        }

        if (weaponry.hasLoadedWeapon()) {
            bots.stat[10] = 0;
            bots.stat[11] = 1;
        }

        if (senses.isBeingDamaged()) {
            bots.stat[12] = 0;
            bots.stat[13] = 1;
        }
    	/*
    	if(bots.runningToPlayer) {
    		bots.stat[14]=0;
    		bots.stat[15]=1;
    	}
    	*/
        bots.stat[14] = (double) bots.hitdamage;
        bots.stat[15] = 1 - bots.stat[14];
        bots.hitdamage = 0;

        lasthitdamage = bots.stat[14];


        //动作状态
        for (int i = 0; i < numAction; i++) {
            if (bots.act[i] == 1) {
                bots.stat[16 + i] = 1;
                bots.stat[16 + numAction + i] = 0;
            }
        }
    }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //动作
    //////////////////
    // STATE ENGAGE //
    //////////////////
    protected void stopshooting(Bots bots) {
        if (info.isShooting() || info.isSecondaryShooting()) {
            getAct().act(new StopShooting());
        }
    }

    /**
     * Fired when bot see any enemy. <ol> <li> if enemy that was attacked last
     * time is not visible than choose new enemy <li> if enemy is reachable and the bot is far - run to him
     * <li> otherwise - stand still (kind a silly, right? :-)
     * </ol>
     */
    protected void stateEngage(Bots bots) {
        //log.info("Decision is: ENGAGE");
        //config.setName("Hunter [ENGAGE]");

        double distance = Double.MAX_VALUE;

        // 1) pick new enemy if the old one has been lost
        if (bots.enemy == null || !bots.enemy.isVisible()) {
            // pick new enemy
            bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());
            if (bots.enemy == null) {
                log.info("Can't see any enemies... ???");
                return;
            }
        }

        // 2) stop shooting if enemy is not visible
        if (!bots.enemy.isVisible()) {
            if (info.isShooting() || info.isSecondaryShooting()) {
                // stop shooting
                getAct().act(new StopShooting());
            }
            bots.runningToPlayer = false;
        } else {
            // 2) or shoot on enemy if it is visible
            distance = info.getLocation().getDistance(bots.enemy.getLocation());
            if (shoot.shoot(weaponPrefs, bots.enemy) != null) {
                log.info("Shooting at enemy!!!");
                bots.shooting = true;
            }
        }

        // 3) if enemy is far or not visible - run to him
        int decentDistance = Math.round(random.nextFloat() * 800) + 200;
        if (!bots.enemy.isVisible() || !bots.shooting || decentDistance < distance) {
            if (!bots.runningToPlayer) {
                navigation.navigate(bots.enemy);
                bots.runningToPlayer = true;
            }
        } else {
            bots.runningToPlayer = false;
            navigation.stopNavigation();
        }

        bots.item = null;
    }

    ///////////////
    // STATE HIT //
    ///////////////
    protected void stateHit(Bots bots) {
        //log.info("Decision is: HIT");
        if (!players.canSeeEnemies()) {
            bot.getBotName().setInfo("HIT");
            if (navigation.isNavigating()) {
                navigation.stopNavigation();
                bots.item = null;
            }
            getAct().act(new Rotate().setAmount(32000));
        } else {
            stateEngage(bots);
        }
    }

    //////////////////
    // STATE PURSUE //
    //////////////////

    /**
     * State pursue is for pursuing enemy who was for example lost behind a
     * corner. How it works?: <ol> <li> initialize properties <li> obtain path
     * to the enemy <li> follow the path - if it reaches the end - set lastEnemy
     * to null - bot would have seen him before or lost him once for all </ol>
     */
    protected void statePursue(Bots bots) {
        //log.info("Decision is: PURSUE");
        if (bots.enemy != null) {
            bot.getBotName().setInfo("PURSUE");
            navigation.navigate(bots.enemy);
            bots.item = null;
        } else {
            //reset();
        }
    }

    protected int pursueCount = 0;

    //////////////////
    // STATE MEDKIT //
    //////////////////

    protected void stateMedKit(Bots bots) {
        //log.info("Decision is: MEDKIT");
        Item item = items.getPathNearestSpawnedItem(ItemType.Category.HEALTH);
        if (item == null) {
            log.warning("NO HEALTH ITEM TO RUN TO => ITEMS");
            stateRunAroundItems(bots);
        } else {
            bot.getBotName().setInfo("MEDKIT");
            navigation.navigate(item);
            bots.item = item;
        }
    }

    ////////////////////////////
    // STATE RUN AROUND ITEMS //
    ////////////////////////////

    protected void stateRunAroundItems(Bots bots) {
        //log.info("Decision is: ITEMS");
        //config.setName("Hunter [ITEMS]");
        if (navigation.isNavigatingToItem()) return;

        bots.interesting = new ArrayList<Item>();

        // ADD WEAPONS
        for (ItemType itemType : ItemType.Category.WEAPON.getTypes()) {
            if (!weaponry.hasLoadedWeapon(itemType)) bots.interesting.addAll(items.getSpawnedItems(itemType).values());
        }
        // ADD ARMORS
        for (ItemType itemType : ItemType.Category.ARMOR.getTypes()) {
            bots.interesting.addAll(items.getSpawnedItems(itemType).values());
        }
        // ADD QUADS
        bots.interesting.addAll(items.getSpawnedItems(UT2004ItemType.U_DAMAGE_PACK).values());
        // ADD HEALTHS
        if (info.getHealth() < 100) {
            bots.interesting.addAll(items.getSpawnedItems(UT2004ItemType.HEALTH_PACK).values());
        }

        Item item = MyCollections.getRandom(tabooItems.filter(bots.interesting));
        if (item == null) {
            log.warning("NO ITEM TO RUN FOR!");
            if (navigation.isNavigating()) return;
            bot.getBotName().setInfo("RANDOM NAV");
            navigation.navigate(navPoints.getRandomNavPoint());
        } else {
            bots.item = item;
            log.info("RUNNING FOR: " + item.getType().getName());
            bot.getBotName().setInfo("ITEM: " + item.getType().getName() + "");
            navigation.navigate(item);
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
 /*   
    //取消敌人和物品，自由移动
    protected void RANDOMNAV(Bots bots) {
    		if(navigation.isNavigating())navigation.stopNavigation();
    		if(navigation.isNavigating())return;
    		bot.getBotName().setInfo("RANDOM NAV");
    		navigation.navigate(navPoints.getRandomNavPoint());   		
    }
    
    
    //找武器，弹药
    protected void stateRunAroundItems(Bots bots) {    	    	
        bots.interesting = new ArrayList<Item>();
        for (ItemType itemType : ItemType.Category.WEAPON.getTypes()) {
        	if (!weaponry.hasLoadedWeapon(itemType)) bots.interesting.addAll(items.getSpawnedItems(itemType).values());
        } 
        
        for (ItemType itemType : ItemType.Category.ARMOR.getTypes()) {
        	bots.interesting.addAll(items.getSpawnedItems(itemType).values());
        }    
        
        bots.interesting.addAll(items.getSpawnedItems(UT2004ItemType.U_DAMAGE_PACK).values()); 
        
        
        Item  item = MyCollections.getRandom(bots.tabooItems.filter(bots.interesting));//***      
        if (item == null) {
        	RANDOMNAV(bots);
        	return;
        } else {
        	bot.getBotName().setInfo("ITEM: " + item.getType().getName() + "");
        	navigation.navigate(item); 
        }
        bots.item = item;
    }
    
    //补血
    protected void stateMedKit(Bots bots) {  	
        Item item = items.getPathNearestSpawnedItem(ItemType.Category.HEALTH);
        items.getSpawnedItems(UT2004ItemType.HEALTH_PACK).values();   
        if (item == null) {
        	RANDOMNAV(bots);
        	return;
        } else {
        	bot.getBotName().setInfo("MEDKIT");
        	navigation.navigate(item);
        }
        bots.item = item;
    }
    
    //追逐,看到人射击
    protected void statePursue(Bots bots) {     	
        if (bots.enemy != null) {
        	bot.getBotName().setInfo("PURSUE");
        	navigation.navigate(bots.enemy);
        }
        	
       if (bots.enemy != null && bots.enemy.isVisible()) {
    	   shoot.shoot(weaponPrefs, bots.enemy);
    	   bots.shooting = true;
       }else {
       	RANDOMNAV(bots);
       }
    }
    
    //转圈，看到人射击
    protected void Rotate(Bots bots) {
        bot.getBotName().setInfo("Rotate");
        getAct().act(new Rotate().setAmount(3000));   
        
        if (bots.enemy != null && bots.enemy.isVisible()) {
	        shoot.shoot(weaponPrefs, bots.enemy);
	        bots.shooting = true;
        }else {
        	RANDOMNAV(bots);
        }
    }
 
    //选择敌人，看到人射击
    protected void stateEngage(Bots bots) {
        bot.getBotName().setInfo("stateEngage");
        
        if (bots.enemy == null || !bots.enemy.isVisible()) {
            bots.enemy = players.getNearestVisiblePlayer(players.getVisibleEnemies().values());
            if (bots.enemy == null) {
            	RANDOMNAV(bots);
            	return;
            }  
        }
        
        if (bots.enemy != null && bots.enemy.isVisible()) {
	        shoot.shoot(weaponPrefs, bots.enemy);
	        bots.shooting = true;
        } else {
        	statePursue(bots);
        }
        
    }

 
     */

    //选择动作
    public void selectact(Bots bots) {
        if (bots.act[0] == 1) {
            stateRunAroundItems(bots);
            return;
        } else if (bots.act[1] == 1) {
            statePursue(bots);
            return;
        } else if (bots.act[2] == 1) {
            stateEngage(bots);
            return;
        } else if (bots.act[3] == 1) {
            stateMedKit(bots);
            return;
        } else if (bots.act[4] == 1) {
            stateHit(bots);
            //stopshooting(bots);
            //RANDOMNAV(bots);
            return;
        }
    	/*
    	else if(bots.act[5]==1) {
    		statePursue(bots);
    		//Rotate(bots);
       		return;
    	}
    	*/
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //奖励
    //相遇，+0.2
    @ObjectClassEventListener(eventClass = WorldObjectAppearedEvent.class, objectClass = Player.class)
    public void playerAppeared(WorldObjectAppearedEvent<Player> event) {
        bot.getBotName().setInfo("playerAppeared");
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        //bots[agt].r=0.2;
    }
/*
	//跟踪
	FlagListener<NavigationState> FlagListener =new FlagListener<NavigationState>(){
		@Override
		public  void flagChanged(NavigationState changedValue) {
		agt=(int)Thread.currentThread().getName().toCharArray()[6]-49;	
        switch (changedValue) {
            case PATH_COMPUTATION_FAILED:
            case STUCK:
                if (bots[agt].item != null) {
                	bots[agt].tabooItems.add(bots[agt].item, 10);
                }
                bots[agt].item = null;
                bots[agt].enemy = null;
                bots[agt].itemsToRunAround = null;
                navigation.stopNavigation();
                break;
            case TARGET_REACHED:
            	bots[agt].item = null;
            	bots[agt].enemy = null;
            	bots[agt].itemsToRunAround = null;
            	navigation.stopNavigation();
                break;
            default:
        	}
		}
	};
	
	   //被打，-0.25
	   IWorldEventListener<BotDamaged> botDamagedListener = new IWorldEventListener<BotDamaged>() {
	       @Override
	       public    void notify(BotDamaged event) {
	    	agt=(int)Thread.currentThread().getName().toCharArray()[6]-49;
	       	//body.getCommunication().sendGlobalTextMessage("I have just been hurt by other bot for: " + event.getDamageType() + "[" + event.getDamage() + "]");
	       	//log.info("I have just been hurt by other bot for: " + event.getDamageType() + "[" + event.getDamage() + "]");
	       	bots[agt].r=-0.25;
	       }
	   };
	   
	   //打人，+0.75
	   IWorldEventListener<PlayerDamaged> PlayerDamagedListener = new IWorldEventListener<PlayerDamaged>() {
		   @Override
		   public   void notify(PlayerDamaged event) {
			   //body.getCommunication().sendGlobalTextMessage("I have just hurt other bot for: " + event.getDamageType() + "[" + event.getDamage() + "]");
			   //log.info("I have just hurt other bot for: " + event.getDamageType() + "[" + event.getDamage() + "]");
		    	agt=(int)Thread.currentThread().getName().toCharArray()[6]-49;
			   bots[agt].r=0.75; 

			    
		   }
	};
	
*/


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////       

    //ai部分
    private void doStep(AGENTS_EMFALCON agent, Bots bots) {

        observe(bots);
        //传入新的状态
        bots.NextStat = bots.stat;
        agent.setState(bots.NextStat);

        agent.max_Q = agent.getMaxQValue(bots.isdead); //计算下次状态最大Q值
        System.out.println("r");
        System.out.println(bots.r);
        agent.new_Q = agent.getNewQValue(bots.r);//Q学习公式


        //代理读取之前的状态
        agent.setState(bots.CurrentStat);
        agent.setAction(bots.action);
        agent.setReward(agent.new_Q);
        //重新记忆
        agent.FALCON_LEARN();
        ///////////////

        observe(bots);
        bots.CurrentStat = bots.stat;

        agent.setState(bots.CurrentStat); //从环境中获得状态和行为   
        bots.action = agent.Policy(); //代理选择Q值

        bots.r = 0;
        bots.DoAction(bots.action);
        selectact(bots);
        //利用Q学习计算Q值
        agent.setAction(bots.action);
        agent.this_Q = agent.FALCON_PERFORM();//计算本状态Q
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //重置
    protected void reset() {
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        bot.getBotName().setInfo("RESET");
        bots[agt].reset();
        navigation.stopNavigation();
        return;
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public TabooSet<Item> tabooItems;
    //跟踪
    FlagListener<NavigationState> FlagListener = new FlagListener<NavigationState>() {
        @Override
        public void flagChanged(NavigationState changedValue) {
            agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
            switch (changedValue) {
                case PATH_COMPUTATION_FAILED:
                    //System.out.println("PATH_COMPUTATION_FAILED");
                case STUCK:
                    //System.out.println("STUCK");
                    if (bots[agt].item != null) {
                        tabooItems.add(bots[agt].item, 10);
                    }
                    bots[agt].item = null;
                    bots[agt].enemy = null;
                    bots[agt].itemsToRunAround = null;
                    navigation.stopNavigation();
                    break;
                case TARGET_REACHED:
                    //System.out.println("TARGET_REACHED");
                    bots[agt].item = null;
                    bots[agt].enemy = null;
                    bots[agt].itemsToRunAround = null;
                    navigation.stopNavigation();
                    break;
                default:
            }
        }
    };

    //主线程准备
    @Override
    public void prepareBot(UT2004Bot bot) {

        autoFixer = new UT2004PathAutoFixer(bot, navigation.getPathExecutor(), fwMap, aStar, navBuilder);

        for (int i = 0; i < agent_num; i++) {
            getWorldView().addEventListener(BotDamaged.class, bots[i].botDamagedListener);
            getWorldView().addEventListener(PlayerDamaged.class, bots[i].PlayerDamagedListener);
            getWorldView().addEventListener(PlayerKilled.class, bots[i].PlayerKilledListener);
            getWorldView().addEventListener(BotKilled.class, bots[i].BotKilledListener);
        }
        tabooItems = new TabooSet<Item>(bot);
        navigation.getState().addListener(FlagListener);
        //getWorldView().addEventListener(BotDamaged.class, botDamagedListener);
        //getWorldView().addEventListener(PlayerDamaged.class, PlayerDamagedListener);
        //navigation.getState().addListener(FlagListener);

        weaponPrefs.addGeneralPref(UT2004ItemType.LIGHTNING_GUN, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.SHOCK_RIFLE, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.MINIGUN, false);
        weaponPrefs.addGeneralPref(UT2004ItemType.FLAK_CANNON, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.ROCKET_LAUNCHER, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.LINK_GUN, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.ASSAULT_RIFLE, true);
        weaponPrefs.addGeneralPref(UT2004ItemType.BIO_RIFLE, true);
    }


    //产生
    @Override
    public void botFirstSpawn(GameInfo gameInfo, ConfigChange config, InitedMessage init, Self self) {
        body.getCommunication().sendGlobalTextMessage("I am alive!");
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //逻辑
    @Override
    public void logic() {
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        doStep(agent[agt], bots[agt]);

    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //初始化
    @Override
    public Initialize getInitializeCommand() {
        agt = (int) Thread.currentThread().getName().toCharArray()[6] - 49;
        reset();
        return new Initialize().setName("Hunter_TDFALCON-" + (++instanceCount)).setDesiredSkill(5);
    }

    //主程序
    private static int agent_num = 2;

    public static void main(String[] args) throws PogamutException, Exception {
        agent = new AGENTS_EMFALCON[agent_num];
        bots = new Bots[agent_num];
        for (int i = 0; i < agent_num; i++) {
            agent[i] = new AGENTS_EMFALCON(i, numState, numAction);
            agent[i].setParameters(TDFALCON, immediateReward);
            bots[i] = new Bots(i, numState, numAction);
        }
        new UT2004BotRunner(HunterBot_EMFALCON.class, "Hunter").setMain(true).setLogLevel(Level.INFO).startAgents(agent_num);
    }


}
